﻿using System;
using System.Collections.Specialized;
using System.Collections.Generic;
using Cms.Application.WebFramework.Presenters;
using Cms.Domain;
using Cms.Tests.Unit.Application;
using Cms.Web.Framework.Infrastructure;
using Cms.Web.Framework.Infrastructure.Wrappers;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;

namespace Cms.Tests.Unit.WebFramework.Infrastructure
{
    [TestClass]
    public class SingleSignOnTests : BasePresenterTests
    {
        [TestMethod]
        public void FirstLogin_NotLoggedInAnywhere_EndUpAtLoginPageForTheRequestedSite()
        {
            InitRepositoryFactoryMock();
            Site fredium = new Site { ID = 1, Domain = "fredium.dev" };
            Site f1 = new Site { ID = 2, Domain = "f1.dev" };
            IList<Site> sites = new List<Site> { fredium, f1 };
            SiteRepositoryMock.Setup(m => m.GetSites()).Returns(sites);
            ICmsFormsAuthentication formsAuthentication = new Mock<ICmsFormsAuthentication>().Object;
            ICmsHttpContextItems contextItems = new Mock<ICmsHttpContextItems>().Object;
            ICmsIdentity identity = new Mock<ICmsIdentity>().Object;
            RequestDetailsPresenter presenter = new RequestDetailsPresenter(FactoryMock.Object);

            // Setup the first request
            Mock<ICmsHttpResponse> responseMock1 = new Mock<ICmsHttpResponse>();
            ICmsHttpRequest request1 = CreateRequestMock("http://fredium.dev/Admin/Default.aspx?ReturnUrl=%2fadmin%2f");
            RequestStartHandler requestHandler1 = new RequestStartHandler(request1, responseMock1.Object, identity, formsAuthentication, contextItems);
            requestHandler1.UnitTestPresenter = presenter;
            requestHandler1.BeginRequest();
            requestHandler1.AuthorizeRequest();
            responseMock1.Verify(m => m.Redirect("http://f1.dev/ssoauth.ashx?origin=%2fadmin%2f&alreadycheckeddomains=fredium.dev"));

            // Setup the second request
            Mock<ICmsHttpResponse> responseMock2 = new Mock<ICmsHttpResponse>();
            ICmsHttpRequest request2 = CreateRequestMock("http://f1.dev/ssoauth.ashx?origin=%2fadmin%2f&alreadycheckeddomains=fredium.dev");
            RequestStartHandler requestHandler2 = new RequestStartHandler(request2, responseMock2.Object, identity, formsAuthentication, contextItems);
            requestHandler2.UnitTestPresenter = presenter;
            requestHandler2.BeginRequest();
            requestHandler2.AuthorizeRequest();
            responseMock2.Verify(m => m.Redirect("http://fredium.dev/admin/default.aspx?ReturnUrl=%2fadmin%2f&ssoauth=completed"));
        }

        [TestMethod]
        public void SwitchSite_LoggedInOnTheFirstSite_AutomaticallyLoggedInOnTheOtherSite()
        {
            InitRepositoryFactoryMock();
            Site fredium = new Site { ID = 1, Domain = "fredium.dev" };
            Site f1 = new Site { ID = 2, Domain = "f1.dev" };
            IList<Site> sites = new List<Site> { fredium, f1 };
            SiteRepositoryMock.Setup(m => m.GetSites()).Returns(sites);
            ICmsFormsAuthentication formsAuthentication = new Mock<ICmsFormsAuthentication>().Object;
            ICmsHttpContextItems contextItems = new Mock<ICmsHttpContextItems>().Object;
            RequestDetailsPresenter presenter = new RequestDetailsPresenter(FactoryMock.Object);
            Administrator admin = new Administrator() { ID = 666, ForceLogout = false, CurrentSite = fredium, Username = "admin", Password = "admin" };
            AdministratorRepositoryMock.Setup(a => a.GetAdministrator(666)).Returns(admin);

            // Setup the first request
            Mock<ICmsHttpResponse> responseMock1 = new Mock<ICmsHttpResponse>();
            ICmsIdentity identity1 = new Mock<ICmsIdentity>().Object;
            ICmsHttpRequest request1 = CreateRequestMock("http://f1.dev/Admin/Default.aspx?ReturnUrl=%2fadmin%2f");
            RequestStartHandler requestHandler1 = new RequestStartHandler(request1, responseMock1.Object, identity1, formsAuthentication, contextItems);
            requestHandler1.UnitTestPresenter = presenter;
            requestHandler1.BeginRequest();
            requestHandler1.AuthorizeRequest();
            responseMock1.Verify(m => m.Redirect("http://fredium.dev/ssoauth.ashx?origin=%2fadmin%2f&alreadycheckeddomains=f1.dev"));

            // Setup the second request
            Mock<ICmsHttpResponse> responseMock2 = new Mock<ICmsHttpResponse>();
            ICmsHttpRequest request2 = CreateRequestMock("http://fredium.dev/ssoauth.ashx?origin=%2fadmin%2f&alreadycheckeddomains=f1.dev");
            RequestStartHandler requestHandler2 = new RequestStartHandler(
                request2, 
                responseMock2.Object, 
                InfrastructureTestsHelper.GetIdentity(666), 
                formsAuthentication, 
                contextItems);
            requestHandler2.UnitTestPresenter = presenter;
            requestHandler2.BeginRequest();
            requestHandler2.AuthorizeRequest();
            responseMock2.Verify(m => m.Redirect(It.IsRegex(@"^http://f1\.dev/Admin/Default\.aspx\?credentials=.*&ReturnUrl=%2fadmin%2f&ssoauth=completed$")));

            // The regex above matches any of these:
            // http://f1.dev/Admin/Default.aspx?credentials=lEQetuMMx755M84bULMmnJn7qJFH%2fis7GASTpP4aFq8%3d&ReturnUrl=%2fadmin%2f&ssoauth=completed
            // http://f1.dev/Admin/Default.aspx?credentials=nuYzu%2fW0ksXgmoFhtRqWylpFyfGBG9EcFdAP7rDFl%2fA%3d&ReturnUrl=%2fadmin%2f&ssoauth=completed
            // http://f1.dev/Admin/Default.aspx?credentials=wbp0zIz%2fQuVk%2bE5A9hlWZXMuGCnwnGhw5g7O4EkvlQo%3d&ReturnUrl=%2fadmin%2f&ssoauth=completed        
        }

        [TestMethod]
        public void JavascriptRequest_NotLoggedInAnywhere_JavascriptIsServed()
        {
            InitRepositoryFactoryMock();
            Site fredium = new Site { ID = 1, Domain = "fredium.dev" };
            Site f1 = new Site { ID = 2, Domain = "f1.dev" };
            IList<Site> sites = new List<Site> { fredium, f1 };
            SiteRepositoryMock.Setup(m => m.GetSites()).Returns(sites);
            ICmsFormsAuthentication formsAuthentication = new Mock<ICmsFormsAuthentication>().Object;
            ICmsCache cache = new Mock<ICmsCache>().Object;
            TestHttpContextItems contextItems = new TestHttpContextItems();
            Mock<ICmsHttpContext> httpContextMock = new Mock<ICmsHttpContext>();
            ICmsIdentity identity = new Mock<ICmsIdentity>().Object;
            RequestDetailsPresenter presenter = new RequestDetailsPresenter(FactoryMock.Object);
            Mock<ICmsHttpResponse> responseMock = new Mock<ICmsHttpResponse>();
            ICmsHttpRequest request1 = CreateRequestMock("http://fredium.dev/WebResource.axd?d=BLf7JqMKnN-8_rF-bBFgVtFpWP9o4Yv_HKKr8UC5jSQgZUZinfZhPYqLMfYDj3PuEv5LCMsvqQEttK8P7gxTyPl2FHWpRFK22bThOPiIhvm2ka3B0&t=634702596454625880");
            RequestStartHandler requestHandler = new RequestStartHandler(request1, responseMock.Object, identity, formsAuthentication, contextItems);
            requestHandler.UnitTestPresenter = presenter;
            requestHandler.BeginRequest();
            requestHandler.AuthorizeRequest();
            responseMock.Verify(m => m.Redirect(It.IsAny<string>()), Times.Never());
            UrlRewriterHandler urlRewriterHandler = new UrlRewriterHandler(
                contextItems.Get("RequestDetails"),
                httpContextMock.Object, 
                formsAuthentication, 
                contextItems, 
                identity, 
                cache);
            urlRewriterHandler.AuthorizeRequest();
            httpContextMock.Verify(m => m.RewritePath(It.IsAny<string>()), Times.Never());
        }

        private class TestHttpContextItems : ICmsHttpContextItems
        {
            private IDictionary<object, object> _items;

            public TestHttpContextItems()
            {
                _items = new Dictionary<object, object>();
            }

            public void Add(object key, object value)
            {
                _items.Add(key, value);
            }

            public void Update(object key, object value)
            {
                _items[key] = value;
            }

            public RequestDetails Get(string key)
            {
                return (RequestDetails)_items[key];
            }
        }

        private ICmsHttpRequest CreateRequestMock(string url)
        {
            Mock<ICmsHttpRequest> requestMock = new Mock<ICmsHttpRequest>();
            requestMock.SetupGet(m => m.ApplicationPath).Returns("/");
            Uri uri = new Uri(url);
            requestMock.SetupGet(m => m.Url).Returns(uri);
            requestMock.SetupGet(m => m.QueryString).Returns(new NameValueCollection());
            return requestMock.Object;
        }
    }
}
